package com.oig.sys.auth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.oig.common.constants.CommonEnum;
import com.oig.common.exception.BadRequestException;
import com.oig.common.util.JsonUtil;
import com.oig.sys.auth.constants.AuthConstants;
import com.oig.sys.auth.constants.AuthEnums;
import com.oig.sys.auth.dao.ITbResourceColumnDao;
import com.oig.sys.auth.dto.ResourceColumnDto;
import com.oig.sys.auth.entity.TbResourceColumn;
import com.oig.sys.auth.entity.TbUserColumn;
import com.oig.sys.auth.service.ITbResourceColumnService;
import com.oig.sys.auth.service.ITbResourceDataCfgService;
import com.oig.sys.auth.service.ITbUserColumnService;
import com.oig.sys.auth.vo.ResourceColumnVo;
import com.oig.sys.auth.vo.UserDataCfgVo;
import com.oig.sys.security.support.SecurityUser;
import com.oig.sys.security.util.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author wuxl
 */
@Service
@Slf4j
public class TbResourceColumnServiceImpl implements ITbResourceColumnService {

	private final ITbResourceColumnDao tbResourceColumnDao;
	private final ITbUserColumnService userColumnService ;
	private final ITbResourceDataCfgService resourceDataCfgService;

	private final RedisTemplate<String, Object> redisTemplate;

	@Autowired
	public TbResourceColumnServiceImpl(ITbResourceColumnDao tbResourceColumnDao,
									   ITbUserColumnService userColumnService,
									   ITbResourceDataCfgService resourceDataCfgService,
									   RedisTemplate<String, Object> redisTemplate) {
		this.tbResourceColumnDao = tbResourceColumnDao;
		this.userColumnService = userColumnService;
		this.resourceDataCfgService = resourceDataCfgService;
		this.redisTemplate = redisTemplate;
	}

	private void addUserResourceColumn(TbResourceColumn resourceColumn){
		List<Integer> userColumnIds = userColumnService.findUserColumnIdByResourceId(resourceColumn.getResourceId());
		if (userColumnIds!=null && !userColumnIds.isEmpty()){
			for (Integer userColumnId : userColumnIds){
				TbResourceColumn userResourceColumn = new TbResourceColumn() ;
				BeanUtils.copyProperties(resourceColumn, userResourceColumn, "id");
                userResourceColumn.setUserColumnId(userColumnId);
				this.tbResourceColumnDao.save(userResourceColumn) ;
			}
		}
	}

	/**
	 * 列管理操作-新增列
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public ResourceColumnVo addResourceColumn(ResourceColumnDto resourceColumnDto) {
		log.debug("dto={}",resourceColumnDto);
		TbResourceColumn resourceColumn = new TbResourceColumn() ;
		BeanUtils.copyProperties(resourceColumnDto, resourceColumn, "id");
		SecurityUser securityUser = SecurityUtil.getDefineUserMain();
		resourceColumn.setCreateUserId(securityUser.getId());
		resourceColumn.setCreateUserName(securityUser.getRealName());
		resourceColumn.setCreateTime(new Date());
		if (resourceColumnDto.getUserColumnId()==null) {
			resourceColumn.setUserColumnId(0);
		}
		resourceColumn.setIsPrimaryKey(resourceColumnDto.getIsPrimaryKey()?CommonEnum.FlagEnum.TRUE_FLAG.getCode():CommonEnum.FlagEnum.FALSE_FLAG.getCode());
		resourceColumn.setIsOrderFlag(resourceColumnDto.getIsOrderFlag()? CommonEnum.FlagEnum.TRUE_FLAG.getCode():CommonEnum.FlagEnum.FALSE_FLAG.getCode());
		resourceColumn.setIsChangeFlag(resourceColumnDto.getIsChangeFlag()?CommonEnum.FlagEnum.TRUE_FLAG.getCode():CommonEnum.FlagEnum.FALSE_FLAG.getCode());
		resourceColumn.setIsShowFlag(resourceColumnDto.getIsShowFlag()?CommonEnum.FlagEnum.TRUE_FLAG.getCode():CommonEnum.FlagEnum.FALSE_FLAG.getCode());
		if (resourceColumnDto.getShowAlign()==null){
			resourceColumn.setShowAlign(AuthEnums.AlignEnum.ALIGN_LEFT.getCode());
		}
		this.tbResourceColumnDao.save(resourceColumn) ;
		//刷新用户的个性化列
		this.addUserResourceColumn(resourceColumn);
		//返回的是通用列
		return this.buildVo(resourceColumn);
	}

	private ResourceColumnVo buildVo(TbResourceColumn resourceColumn){
		ResourceColumnVo resourceColumnVo = new ResourceColumnVo();
		BeanUtils.copyProperties(resourceColumn, resourceColumnVo);
		if (CommonEnum.FlagEnum.TRUE_FLAG.getCode().equals(resourceColumn.getIsPrimaryKey())){
			resourceColumnVo.setIsPrimaryKey(true);	//默认值为false
		}
		if (CommonEnum.FlagEnum.TRUE_FLAG.getCode().equals(resourceColumn.getIsOrderFlag())){
			resourceColumnVo.setIsOrderFlag(true);	////默认值为false
		}
		if (CommonEnum.FlagEnum.FALSE_FLAG.getCode().equals(resourceColumn.getIsChangeFlag())){
			resourceColumnVo.setIsChangeFlag(false);	//默认值为true
		}
		if (CommonEnum.FlagEnum.FALSE_FLAG.getCode().equals(resourceColumn.getIsShowFlag())){
			resourceColumnVo.setIsShowFlag(false);	//默认值为true
		}
		return resourceColumnVo;
	}

	/**
	 * 列管理操作-修改列
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public ResourceColumnVo updateResourceColumnByCode(ResourceColumnDto resourceColumnDto) {
		//更新的不只是当前这条数据
		TbResourceColumn resourceColumn = this.tbResourceColumnDao.getRsColumnById(resourceColumnDto.getId());
		log.debug("dto={},entity={}",resourceColumnDto, resourceColumn);
		if (!resourceColumn.getResourceId().equals(resourceColumnDto.getResourceId())){
			throw new BadRequestException("不能修改所属菜单");
		}
		if (resourceColumn.getUserColumnId()!=null && resourceColumn.getUserColumnId()!=0){
			throw new BadRequestException("列所属用户个性化id错误");
		}
		BeanUtils.copyProperties(resourceColumnDto, resourceColumn, "id");
		resourceColumn.setIsPrimaryKey(resourceColumnDto.getIsPrimaryKey()?CommonEnum.FlagEnum.TRUE_FLAG.getCode():CommonEnum.FlagEnum.FALSE_FLAG.getCode());
		resourceColumn.setIsShowFlag(resourceColumnDto.getIsShowFlag() ? CommonEnum.FlagEnum.TRUE_FLAG.getCode():CommonEnum.FlagEnum.FALSE_FLAG.getCode());
		resourceColumn.setIsChangeFlag(resourceColumnDto.getIsChangeFlag() ? CommonEnum.FlagEnum.TRUE_FLAG.getCode():CommonEnum.FlagEnum.FALSE_FLAG.getCode());
		resourceColumn.setIsOrderFlag(resourceColumnDto.getIsOrderFlag() ? CommonEnum.FlagEnum.TRUE_FLAG.getCode():CommonEnum.FlagEnum.FALSE_FLAG.getCode());
		resourceColumn.setLastUpdateId(SecurityUtil.getDefineUserMain().getId());
		resourceColumn.setLastUpdateName(SecurityUtil.getDefineUserMain().getRealName());
		resourceColumn.setLastUpdateTime(new Date());
		this.tbResourceColumnDao.save(resourceColumn) ;
		this.tbResourceColumnDao.updateResourceColumnByField(resourceColumn) ;

		if (!resourceColumnDto.getIsChangeFlag()){	//如果改成不能个性化，把个性化相关的字段初始化一下
			this.tbResourceColumnDao.updateResourceColumnForNoChangeByField(resourceColumn) ;
		}

		return this.buildVo(resourceColumn);
	}


	/**
	 * 数据量不多，先简单循环save
	 * 列管理操作-新增菜单时，批量新增列
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public List<ResourceColumnVo> addResourceColumn(Integer resourceId, List<ResourceColumnDto> resourceColumnDtoList) {
		List<ResourceColumnVo> resourceColumnVoList = new ArrayList<>() ;
		for (ResourceColumnDto resourceColumnDto : resourceColumnDtoList){
			resourceColumnDto.setResourceId(resourceId);
			ResourceColumnVo resourceColumnVo = this.addResourceColumn(resourceColumnDto) ;
			resourceColumnVoList.add(resourceColumnVo) ;
		}
		// 同步缓存
		changeAllColumnCache(resourceId, resourceColumnVoList);
		return resourceColumnVoList;
	}

	/**
	 * 列管理操作-修改菜单时，对应列的批量修改
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public List<ResourceColumnVo> updateResourceColumnByCode(Integer resourceId, List<ResourceColumnDto> resourceColumnDtoList) {
		List<ResourceColumnVo> resourceColumnVoList = new ArrayList<>() ;
		List<Integer> columnIdList = new ArrayList<>() ;
		for (ResourceColumnDto resourceColumnDto : resourceColumnDtoList){
			resourceColumnDto.setResourceId(resourceId);
			ResourceColumnVo resourceColumnVo ;
			if (resourceColumnDto.getId()==null){		//如果是新增的
				resourceColumnVo = this.addResourceColumn(resourceColumnDto) ;
			} else {
				resourceColumnVo = this.updateResourceColumnByCode(resourceColumnDto) ;
			}
			resourceColumnVoList.add(resourceColumnVo) ;
			columnIdList.add(resourceColumnVo.getId()) ;
		}
		//查询出界面已删除的列代码
		List<String> delColumnFieldList = this.tbResourceColumnDao.findDelColumnFieldById(columnIdList, resourceId);
		if (delColumnFieldList!=null && !delColumnFieldList.isEmpty()){
			//根据列代码查询要删除的列id
			List<Integer> delColumnIdList = this.tbResourceColumnDao.findDelColumnIdsByCode(resourceId, delColumnFieldList) ;
			if (delColumnIdList!=null && !delColumnIdList.isEmpty()){
				//根据id删除列
				this.tbResourceColumnDao.deleteByIdList(delColumnIdList);
			}
		}

		// 同步缓存
		changeAllColumnCache(resourceId, resourceColumnVoList);
		return resourceColumnVoList;
	}


	private ResourceColumnVo updateUserColumn(ResourceColumnDto resourceColumnDto, TbUserColumn userColumn){
		log.debug("resourceColumnDto :{}",resourceColumnDto);
		TbResourceColumn userResourceColumn = tbResourceColumnDao.getByResourceIdAndUserColumnIdAndColumnField(userColumn.getResourceId(),userColumn.getId(),resourceColumnDto.getColumnField()) ;
		if (userResourceColumn == null) {
			throw new BadRequestException("个性化列" + resourceColumnDto.getColumnField() + "数据不存在，刷新页面后重试");
		}
		boolean isChanged = false;
		if(resourceColumnDto.getIsShowFlag() && CommonEnum.FlagEnum.FALSE_FLAG.getCode().equals(userResourceColumn.getIsShowFlag())){
			isChanged = true;
		} else if (!resourceColumnDto.getIsShowFlag() && CommonEnum.FlagEnum.TRUE_FLAG.getCode().equals(userResourceColumn.getIsShowFlag())){
			isChanged = true;
		}
		if (StringUtils.isNotBlank(resourceColumnDto.getShowWidth()) && !resourceColumnDto.getShowWidth().equals(userResourceColumn.getShowWidth())){
			isChanged = true;
		}
		if (resourceColumnDto.getListSort()!=null && !resourceColumnDto.getListSort().equals(userResourceColumn.getListSort())){
			isChanged = true;
		}
		if (resourceColumnDto.getQuerySort()!=null && !resourceColumnDto.getQuerySort().equals(userResourceColumn.getQuerySort())){
			isChanged = true;
		}
		if (resourceColumnDto.getQueryShowFlag()!=null && !resourceColumnDto.getQueryShowFlag().equals(userResourceColumn.getQueryShowFlag())){
			isChanged = true;
		}
		if (isChanged){
			BeanUtils.copyProperties(resourceColumnDto, userResourceColumn, "id", "enumValueJson", "columnValueType", "queryType");
			userResourceColumn.setIsShowFlag(resourceColumnDto.getIsShowFlag() ? CommonEnum.FlagEnum.TRUE_FLAG.getCode():CommonEnum.FlagEnum.FALSE_FLAG.getCode());
			userResourceColumn.setLastUpdateTime(new Date());
			userResourceColumn.setLastUpdateId(SecurityUtil.getDefineUserMain().getId());
			userResourceColumn.setLastUpdateName(SecurityUtil.getDefineUserMain().getRealName());
			this.tbResourceColumnDao.save(userResourceColumn) ;
		}
		return this.buildVo(userResourceColumn);
	}



	private TbUserColumn addUserColumn(Integer resourceId){
		log.debug("addUserColumn :{}", resourceId);
		TbUserColumn userColumn = userColumnService.addUserColumn(resourceId, SecurityUtil.getDefineUserMain().getId());
		List<TbResourceColumn> resourceColumnList = this.tbResourceColumnDao.findBaseColumnByResourceId(resourceId) ;
		if (resourceColumnList==null || resourceColumnList.isEmpty()){
			throw new BadRequestException("菜单不存在列表数据") ;
		}
		for(TbResourceColumn resourceColumn : resourceColumnList){
			TbResourceColumn userResourceColumn = new TbResourceColumn();
			BeanUtils.copyProperties(resourceColumn, userResourceColumn, "id");
			userResourceColumn.setUserColumnId(userColumn.getId());
			userResourceColumn.setCreateTime(new Date());
			userResourceColumn.setLastUpdateTime(userResourceColumn.getCreateTime());
			this.tbResourceColumnDao.save(userResourceColumn);
		}
        return userColumn;
	}

	/**
	 * 返回菜单显示列
	 * @param resourceId 菜单id
	 */
	@Override
	public List<ResourceColumnVo> findAllColumnByResourceId(Integer resourceId) {
		List<ResourceColumnVo> resourceColumnList = getColumnByCache(resourceId, null);
		if (CollectionUtils.isEmpty(resourceColumnList)) {
			log.debug("findAllColumnByResourceId get data by database");
			List<TbResourceColumn> tbResourceColumns = this.tbResourceColumnDao.findBaseColumnByResourceId(resourceId);
			resourceColumnList = this.buildVoList(tbResourceColumns, null);
			setColumnCache(resourceId, null, resourceColumnList);
		}else {
			log.debug("findAllColumnByResourceId get data by cache");
		}
		return resourceColumnList;
	}

	private List<ResourceColumnVo> buildVoList(List<TbResourceColumn> resourceColumnList,UserDataCfgVo userDataCfgVo){
		List<ResourceColumnVo> resourceColumnVoList = new ArrayList<>();
		if (CollectionUtil.isNotEmpty(resourceColumnList)) {
			for (TbResourceColumn resourceColumn : resourceColumnList){
				log.debug("resourceColumn:{}", resourceColumn);
				if (userDataCfgVo!=null&&userDataCfgVo.getExcludeFields()!=null
						&&userDataCfgVo.getExcludeFields().contains(resourceColumn.getColumnField())){
					log.debug("exclude field:{}", resourceColumn.getColumnField());
					continue;
				}
				resourceColumnVoList.add(this.buildVo(resourceColumn)) ;
			}
		}
		return resourceColumnVoList;
	}

	/**
	 * 返回用户的菜单显示列
	 * @param resourceId 菜单id
	 * @param userId 用户id
	 */
	@Override
	public List<ResourceColumnVo> findAllColumnByResourceId(Integer resourceId, Integer userId, boolean addExclude) {
		List<ResourceColumnVo> resourceColumnList = getColumnByCache(resourceId, userId);
		if (CollectionUtils.isEmpty(resourceColumnList)) {
			log.debug("findAllColumnByResourceId get data by database");
			TbUserColumn userColumn = this.userColumnService.getByResourceIdAndUserId(resourceId, userId);
			log.debug("userColumn :{}", userColumn);
			UserDataCfgVo userDataCfgVo = null;
			if (addExclude) userDataCfgVo = resourceDataCfgService.getUserDataCfg(resourceId);
			if (userColumn != null) {
				List<TbResourceColumn> tbResourceColumns = this.tbResourceColumnDao.findUserColumnByResourceId(resourceId, userColumn.getId());
				resourceColumnList = this.buildVoList(tbResourceColumns, userDataCfgVo);
				setColumnCache(resourceId, userId, resourceColumnList);
			} else {
				List<TbResourceColumn> tbResourceColumns = this.tbResourceColumnDao.findBaseColumnByResourceId(resourceId);
				resourceColumnList = this.buildVoList(tbResourceColumns, userDataCfgVo);
				setColumnCache(resourceId, null, resourceColumnList);
			}
		} else {
			log.debug("findAllColumnByResourceId get data by cache");
		}
		return resourceColumnList;
	}



	/**
	 * 更新个性化列表展示信息
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public List<ResourceColumnVo> changeAllColumn(List<ResourceColumnDto> resourceColumnDtoList) {
		List<ResourceColumnVo> resourceColumnVoList = new ArrayList<>();
		TbUserColumn userColumn = userColumnService.getByResourceIdAndUserId(resourceColumnDtoList.get(0).getResourceId(),SecurityUtil.getDefineUserMain().getId());
		if (userColumn==null){
			userColumn=this.addUserColumn(resourceColumnDtoList.get(0).getResourceId());
		}
		for (ResourceColumnDto resourceColumnDto : resourceColumnDtoList) {
			resourceColumnDto.setUserColumnId(userColumn.getId());
			ResourceColumnVo resourceColumnVo = this.updateUserColumn(resourceColumnDto, userColumn);
			resourceColumnVoList.add(resourceColumnVo);
		}
		// 更新缓存
		List<TbResourceColumn> cacheColumnVos = tbResourceColumnDao.findUserColumnByResourceId(resourceColumnDtoList.get(0).getResourceId(), userColumn.getId());
		setColumnCache(userColumn.getResourceId(), userColumn.getUserId(), BeanUtil.copyToList(cacheColumnVos, ResourceColumnVo.class));
		return resourceColumnVoList;
	}


	@Transactional(rollbackFor = Exception.class)
	@Override
	public void delAllColumnByResourceId(Integer resourceId) {
		this.tbResourceColumnDao.deleteByResourceId(resourceId);	//可能太多需要分页按id删，先简单按resourceId直接删
		List<Integer> userColumnIds = userColumnService.findUserColumnIdByResourceId(resourceId);
		if (userColumnIds!=null && !userColumnIds.isEmpty()){
			this.userColumnService.deleteByIds(userColumnIds);
		}
		// 删除缓存
		delColumnCache(resourceId);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void delAllColumnByResourceIdList(List<Integer> resourceIdList) {
		if (resourceIdList!=null&&!resourceIdList.isEmpty()){
			for (Integer resourceId : resourceIdList) {
				delAllColumnByResourceId(resourceId);
			}
		}
	}

	private List<ResourceColumnVo> getColumnByCache(Integer resourceId, Integer userId) {
		try {
			log.debug("getColumnByCache resourceId is {},userId is {}", resourceId, userId);
			String key = String.format(AuthConstants.AUTH_RESOURCE_REDIS_PREFIX, resourceId);
			String userKey = (userId != null && userId!= 0) ? (key + ":" + userId) : key;
			Object ueserValue = redisTemplate.opsForValue().get(userKey);
			List<ResourceColumnVo> list = deserializeValue(ueserValue);
			if (CollectionUtils.isEmpty(list)){
				Object value = redisTemplate.opsForValue().get(key);
				return deserializeValue(value);
			}
			return list;
		} catch (Exception e) {
			log.error("从缓存中获取用户展示列失败, resourceId is {},userId is {}", resourceId, userId);
		}
		return null;
	}

	private List<ResourceColumnVo> deserializeValue(Object value) {
		if (Objects.nonNull(value)) {
			return JsonUtil.toList(value.toString(), ResourceColumnVo.class);
		}
		return null;
	}

	private void changeAllColumnCache(Integer resourceId, List<ResourceColumnVo> list) {
		if (!CollectionUtils.isEmpty(list)) {
			setColumnCache(resourceId, null, list);
			// 同步用户个性化缓存
			List<TbUserColumn> tbUserColumns = userColumnService.getByResourceId(resourceId);
			if (!CollectionUtils.isEmpty(tbUserColumns)) {
				for (TbUserColumn tbUserColumn : tbUserColumns) {
					List<TbResourceColumn> columnList = tbResourceColumnDao.findUserColumnByResourceId(resourceId, tbUserColumn.getId());
					setColumnCache(resourceId, tbUserColumn.getUserId(), BeanUtil.copyToList(columnList, ResourceColumnVo.class));
				}
			}
		}
	}

	private void setColumnCache(Integer resourceId, Integer userId, List<ResourceColumnVo> list) {
		if (!CollectionUtils.isEmpty(list)){
			String key = String.format(AuthConstants.AUTH_RESOURCE_REDIS_PREFIX, resourceId);
			if (userId != null && userId != 0) {
				key = key + ":" + userId;
			}
			String value = JsonUtil.toJSONString(list);
			log.debug("setColumnCache resourceId is {},userId is {}", resourceId, userId);
			redisTemplate.opsForValue().set(key, value);
		}
	}

	private void delColumnCache(Integer resourceId) {
		log.debug("delColumnCache resourceId is {}", resourceId);
		String key = String.format(AuthConstants.AUTH_RESOURCE_REDIS_PREFIX, resourceId);
		List<Integer> userIds = userColumnService.findUserIdByResourceId(resourceId);
		if (!CollectionUtils.isEmpty(userIds)) {
			log.debug("delColumnCache userIds is {}", JsonUtil.toJSONString(userIds));
			for (Integer userId : userIds) {
				redisTemplate.opsForValue().getAndDelete(key + ":" + userId);
			}
		}
		redisTemplate.opsForValue().getAndDelete(key);
	}
}
